Whenever directory data is loaded into memory, the filing system performs a number of checks to ensure that it really represents a valid directory - incorrect directories could have disastrous effects. An error message of the form directory data in 'ADFS::4.$.A0.A0.A0' is invalid is given in this case.
Before a new file or directory is written to the host filing system, raFS also ensures that the directory doesn't contain any files with invalid names. If a file is found whose name is not one of the 77 names A0 to Z1, the save operation fails with Name inside the directory containing a raFS disc is not of the form 'A0.A0.A0'
Finally, special code deals with files on raFS which refer to files on the host filing system that either don't exist or that have a different time/date stamp. If a file is missing, a dummy file (length zero, file type zero, date stamp 01-01-1900) is created to prevent other files from being assigned that ID. (Depending on the configured TimeZone of your machine, the year may also be displayed as 2248 instead of 1900.)
If the date stamp, file type or the file length does not match the directory entry, all open, load, save, delete and create operations on files will only produce an error like 'ADFS::4.$.A0.A0.A0' does not belong to this directory entry - verify the raFS disc. In this state, the only possible operations are renaming the file, stamping it (or changing its access details) and deleting it (only if it refers to a dummy file). Stamping the file causes the data to be synchronized, so that any operation can be applied to it again, but only do this if you are very sure about it!
Because there are filing systems that cannot store the date stamp in centiseconds like the native RiscOS filing systems do, the stamp will still be considered valid if it doesn't differ by more than 24 hours from what the raFS directory entry contains. Some filing systems don't allow file stamps at all. Consequently, it is possible to disable the checks by setting the system variable raFS$NoChecks to a comma-separated list of filing system names or image file types of the form &FC8 before mounting discs on those filing systems. The names are matched case-sensitively.
raFS claims FindV (called by OS_Find) and FSCV (called by OS_FSControl) and will close its own files before FileSwitch closes them on the host filing system. This is done either only for one filing system in the case of OS_Find 0,0 (close all files on current filing system), or for all filing systems in the case of OS_FSControl 22 (close all open files) and OS_FSControl 23 (shutdown all filing systems).
a%=OPENIN"ADFS::4.$.
Filename"
SYS"OS_Args",6,a%,512<<10
Whereas FileCore ensures the specified size regardless of whether or not write access was granted, raFS gets it right and ignores FSEntry_Args 6 if the file is read-only. (By the way: Could this be used to compromise security when a FileCore-based filing system is shared over a network? Just a thought...)
I haven't found a bug responsible for this, but I'm aware that if there is one, it's a serious problem. The path switch that was added to Mount will hopefully provide a work-around for this problem.
A similar thing happens when you try to rename an object whose file isn't found on the host filing system. This is a bug in the Filer_Action module. (The module's programmer assumed that there will never be an error under certain circumstances.)
In practice, this is very unlikely to happen.
The reason for the problem might lie in the fact that Memphis might not expect that data saved to it is somewhere in the sprite area: raFS makes a call to FileSwitch to save data to Memphis. Upon receiving the query from FileSwitch, Memphis does some housekeeping, during which the size of one of its sprites is altered, so that the sprite that belongs to raFS is copied to a different memory location. Then Memphis "saves" the area of memory specified by FileSwitch, but this no longer points to the correct data! The correct behaviour of Memphis would be to check whether the data to be saved is in the sprite area, and if it is, to update its pointer to it whenever a sprite with a lower address changes size.
[By the way: At present, raFS itself doesn't perform these checks either...]
It should be possible to use Memphis at the same time as raFS as long as it is not the host filing system. However, don't blame me if something screws up...
The raFSFiler module provides one *command:
The -choices and -newdisc switches are useful for executing when the icon bar icon is clicked on. They open the "Choices" and "Create new disc" windows, respectively.
Desktop_raFSFiler also supports -options, -mount, -select and -adjust switches, but these are intended for internal use only.
raFS provides several *commands. While the raFSFiler module offers a more convenient way of working with discs, you may sometimes prefer to use the command line instead.
The storage directory is the directory to which all files saved to the disc will be written. If the specified directory does not already exist, it is created. If it isn't empty, raFS will give an error.
If the given directory's leafname begins with "!", the optional -app switch can be used to specify the name of a sprite to use for this application, for example -app directory to make it look like an ordinary directory. This sprite must be present in the Wimp's pool when the command is issued. raFS also saves a !Run file so that double-clicking on the application will mount the disc and open its root directory. The -app switch has no effect if the first character of the leafname is not "!".
To delete a complete raFS disc, make sure it has been dismounted and then Wipe the storage directory.
Future versions of raFS may print out additional information after that described above, with further items appended to the printed lines after spaces. Take this into account when writing programs that make use of this command's output.
Two discs with the same name cannot be mounted at the same time - an error will be given if this is attempted. However, if you try to mount a disc that is already mounted (i. e. the disc of that name has the same storage directory as specified in the Mount command), raFS will give no error and will ignore the command.
raFS automatically decides whether or not the disc is read-only by looking at the file !Atterer. If this file does not have write access (either because it resides on a read-only filing system or because you set its attributes in order to permanently "write protect" the disc), all attempts to modify files or directories will be faulted.
The optional -readonly switch (short -ro) write protects the disc even if !Atterer has write access.
Any -path switch appended to the command will only come into effect if the disc stored in the specified directory has the same name as one of the currently mounted discs. In this case, it is assumed that the name of that disc's storage directory has changed (e. g. because you renamed a directory in the path) and that raFS should update its workspace accordingly. Use this with care! You must not use this feature to set the path to the storage directory of a different disc!
When mounting discs without an -X switch appended to the Mount command, raFS looks for a file called !Mount in the storage directory. If the file is not present, the disc is just mounted, but if !Mount is found, it is Run. Apart from that, nothing happens; the disc is not mounted if !Mount is run. However, it can be an Obey file containing the command "raFS:Mount <Obey$Dir> -X". The presence of the -X switch indicates to raFS that the disc should be mounted without searching for !Mount. (Be careful not to create a loop by forgetting to add the -X switch!) With this system, it is possible to execute *commands before and after the disc is mounted.
Note that in case raFS is RMKilled, it will try to dismount all discs. However, any errors while doing this will be ignored, and the module will not refuse to die, which means you lose data! It behaves like this because if it refused to be RMKilled, you would not be able to reload it unless you reset the machine.
Before a disc is dismounted, raFS sets the system variable raFS$DDisc to its name and tries to run any file called !Dismount residing in the storage directory. If errors occur during its execution, they are ignored; the disc is always dismounted. You must not dismount raFS discs in a !Dismount file. When the module is RMKilled, the !Dismount files are not executed.
The -verbose switch has the same functionality as for raFS_Safe below.
A -verbose switch makes raFS print out the value of the internal counter after it has been modified.
raFS_Unsafe and raFS_Safe can be added to a Wimp application's !Run file if the application tends to crash the computer, in such a way that write-through is disabled once the application is quit:
other commands, e. g. WimpSlot, IconSprites etc.
raFS_Unsafe
Run <
App$Dir>.!RunImage %*0
raFS_Safe
Currently, the following keywords have an effect:
If both this and -DirsaveMods below are enabled at the same time, the directory will be saved when the delay or the number of changes is reached, whichever comes first.
When the number of changes is counted, stamping of files, changing their access details or closing open files is not taken into account. This is necessary to ensure a Filer copy operation only increases the count by 1.
By the way: Have you noticed that RiscOS does not have an Upcall for informing that an object's size has changed? I'm using OS_Upcall 3,1 (Writing catalogue information), but even this excludes the size.
You can use -LoadMessages null: to discard the messages and revert to the default ones.
As of version 1.10, there is a way of calling certain routines inside the raFS module directly from assembler. raFSFiler uses these calls to get information about discs and current settings.
To call an raFS routine, you must first find out the address of the module workspace with:
SYS "XOS_Module",18,"raFS" TO R0%,R1%,R2%,R3%,workspace%
Move the returned value into R11 before calling raFS with the following instructions:
MOV LR,PC
LDR PC,[R11,#rout_offset%]
If you use MOV LR,PC
(and not ADD LR,PC,#0
) before calling the routine, the N, Z and C flags will be preserved. When calling raFS from SVC code, you must use MOV
, otherwise the processor will switch to USR mode once the call returns.
The rout_offset%
is zero for the routine described first below, and increases in the order the routines are described. The Docs directory contains a small BASIC program which sets up the offsets with a horrible EVAL trick and which also provides a macro for the BASIC assembler so the above can be reduced to, e. g. FNcall(raFS_Info)
.
Registers not mentioned in "On exit" are preserved. (Of course, R14 is corrupted.) For all calls, R11 must point to the workspace of the raFS module. Processor must be in USR or SVC mode, R13 must point to a full descending stack with at least 1k free.
In the following descriptions, any bits in fields that are not described are reserved. It should not be assumed that they are zero, and when setting the word's value, they must be preserved.
raFS_Info
On exit:
R0 = module version number * 100
R1 = maximum number of discs this version can have mounted
R2 = bit 0 set => module's heap is in sprite area, bits 24-31 = country number (1 for UK, 7 for Germany / as of V1.12, is always 1)
R3-R5 corrupted
Never returns an error.
raFS_NrOfDiscs
On exit:
R0 = number of discs currently mounted
Never returns an error.
raFS_EnumerateDiscs
On exit:
R0 = word-aligned pointer to zero-terminated disc name
R1 = word-aligned pointer to unterminated name of storage directory
R2 = length of name of storage directory
R9 = disc number (also value of R9 for next call to this routine) or -1 and C set if no more discs
Never returns an error. The information must be copied away immediately; it may change during any call to raFS (except raFS_MemCopy) or accesses to the filing system.
After the disc name there are 1 to 4 zero bytes, up to the next word boundary, so you can find the length faster by first loading words and only looking at the high byte with TST Rx,#&FF<<24
raFS_FindDisc
On exit:
R0 = word-aligned pointer to zero-terminated disc name
R1 = word-aligned pointer to unterminated name of storage directory
R2 = length of name of storage directory
R8 = internal ID of the disc's root directory
R9 = disc number
R14 = length of disc name
If a disc of the given name is not mounted, an error is returned. Again, the returned pointers to the disc name (whose case may differ from that passed to the routine) and storage directory are read-only and must be copied away immediately.
raFS_DiscInfo
On exit:
R0 = word-aligned pointer to zero-terminated disc name
R1 = word-aligned pointer to unterminated name of storage directory
R2 = length of name of storage directory
R3 = flags:
bit 0 set => the disc is read-only
bit 1 set => integrity checks are performed (raFS$NoChecks)
R8 = internal ID of the disc's root directory
or C set if disc of that number not mounted
If the given disc number is in the valid range (zero to maximum number - 1) and a disc of that number does not exist, this returns with C set. Obviously, this call is much faster than raFS_FindDisc because it doesn't have to search for the disc name.
raFS_MemCopy
On exit:
Registers preserved
The memory areas may overlap.
raFS_ReadVar
On exit:
R0 = value / pointer to string (depending on variable type)
R1 corrupted
Currently, the following variables are supported:
raFS_SetVar
On exit:
R0, R1 corrupted
R0 on entry contains the same values as for raFS_ReadVar
Size Description 64 disc name, null terminated, padded with zeroes 4 there is guaranteed no unused ID lower than this one 4 attribute flags for root directory (0 at present) 4 0 (unimplemented) 4 o = number of buffer settings following (always 0 at present) ??*o buffer settings
Size Description 4 n = nr of entries 4 m = 0 (unimplemented) 4 ID of parent (max. 76.76.76.0) - word contains -1 for root 4 &80000000 (unimplemented) 4*n offset to start of directory entries (words must be sorted in ascending order, file names in ASCII order, offsets are relative to start of these offsets) 4*m if any, offset to... (unimplemented) 4 offset to first unused byte after dir entries/dir names (also from beginning of offsets, so that if there are no entries, this contains the value 4)
This is followed by the individual entries, if any:
4 load address 4 exec address 4 length (or FileSwitch handle if flag bit 12 set) 4 attributes 4 raFS attributes/flags: bits 0-1: 0 (unimplemented) 2-3: 0 (unimplemented) 4-5: 0 (unimplemented) 6-7: 0=normal file, 1/2 unimplemented, 3=directory 12: if set, file is open => 'length' contains FileSwitch handle 4 0 (unimplemented) 4 ID of file/directory 4*? name of file/directory, zero-terminated, padded with zeroes to the next multiple of 4. 4*? (unimplemented, always null at present)
If an entry with bit 12 set is encountered upon loading directory data, raFS checks whether its own list of files contains that file ID together with the FileSwitch handle found in the directory entry. If there is no such entry in the list of open files, it is assumed that the machine crashed and the length is restored.